package xj.graph2d.tools;

import static javax.swing.SwingConstants.VERTICAL;
import static xj.app.BaseApplicationFrame.getResourceString;
import static xj.graph2d.app.resources.Style.font;
import static xj.graph2d.DrawShapeConstants.PointOnHorizontalDivider;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
import java.util.Observer;

import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

import xj.graph2d.Document;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.GraphComponentShape;
import xj.graph2d.GraphDocument;
import xj.graph2d.GraphEdgeBase;
import xj.graph2d.GraphView;
import xj.graph2d.Label;
import xj.graph2d.ModelListener;
import xj.graph2d.TextShape;
import xj.graph2d.app.DrawApp;
import xj.graph2d.app.DrawAppView;
import xj.graph2d.app.GraphAppView;
import xj.graph2d.command.ChangeEdgeShapeCommand;
import xj.graph2d.command.FlipEdgeCommand;
import xj.graph2d.command.ReverseEdgeCommand;
import xj.graph2d.command.SwapEdgeEndsCommand;
import xj.graph2d.shapes.BarShape;
import xj.graph2d.shapes.BoxLabel;
import xj.graph2d.shapes.GraphEdgeShape;
import xj.graph2d.shapes.HorizontalCompartments;
import xj.graph2d.shapes.PolyShape;
import xj.graph2d.shapes.ShapeIcon;
import xj.graph2d.shapes.block.BlockShape;
import xj.graph2d.shapes.block.StatementShape;
import xj.graph2d.shapes.uml.ClassShape;
import xj.graph2d.shapes.uml.StateShape;
import xj.graph2d.shapes.uml.OrthogonalStateShape;
import xj.graph2d.template.GraphEdgeTemplate;
import xj.graph2d.template.Template;
import xj.graph2d.template.uml.UMLTransitionModelListener;
import xj.meta.ModelElement;
import xj.model.uml.ActivityDiagram;
import xj.model.uml.ActivityFlow;
import xj.model.uml.ActivityNode;
import xj.model.uml.Actor;
import xj.model.uml.ClassDiagram;
import xj.model.uml.Enumeration;
import xj.model.uml.ExecutionOccurance;
import xj.model.uml.HyperState;
import xj.model.uml.LifeLine;
import xj.model.uml.Note;
import xj.model.uml.OrthogonalState;
import xj.model.uml.SequenceDiagram;
import xj.model.uml.State;
import xj.model.uml.StateDiagram;
import xj.model.uml.SubmachineState;
import xj.model.uml.Transition;
import xj.model.uml.UMLAssociation;
import xj.model.uml.UMLClass;
import xj.model.uml.UMLInterface;
import xj.model.uml.UMLMessage;
import xj.model.uml.UMLObject;
import xj.model.uml.UMLPackage;
import xj.model.uml.UMLSystem;
import xj.model.uml.UMLSystemBoundary;
import xj.model.uml.UseCase;
import xj.model.uml.UseCaseDiagram;

public class UMLPointTool 
  extends PointTool {

  public UMLPointTool() {    
  }

  /**
   * Popup for selected shape, trigger by right click or ctrl-click  
   */
  @Override
  public void popupTriggered(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("UMLPointTool.popupTriggered() p=" + p);
    }

    if (showOverHint) {
      overShape = null;
    }
    hotspotShape = null;

    unsetSelection();
    start(observer);
    auxActions.clear();

    if (view != null) {
      displayLocation(p);
      hidePopup();

      view.hideThumbNail();
      mousePosition = p;
      cur = view.physicalToVirtual(p);
      setSelection(modifiers);

      JPopupMenu popup = getContextSensitivePopup();
      if (popup != null) {	
	popup.show(view, p.x, p.y);
      }
    }
  }

  @Override
  public void start(Observer observer) {
    super.start(observer);
    
    if (observer instanceof DrawAppView) {
      DrawAppView dview = (DrawAppView) observer;
      main = dview.getMain();
    } else if (observer instanceof GraphAppView) {
      GraphAppView gview = (GraphAppView) observer;
      main = gview.getMain();
    }
    if (main != null) { 
      hoverPopupManager = main.getHoverPopupManager();
      hoverPopupManager.setTool(this);
    }

  }

  @Override
  public void deactivate(Observer observer) {
    super.deactivate(observer);
    hidePopup();
  }

  @Override
  protected void handleShapeSelected(DrawShape selectedShape) { 
    if (doc.getDocumentType() == Document.DocumentType.Visual_Class_Diagram) { 
      if (overShape instanceof TextShape) { 
	setDisplayMode((TextShape) overShape, EDITING);
      }
    }
  }

  @Override
  protected void handleShapeDeselected(DrawShape prevSelectedShape) { 
    resetDisplayMode(prevSelectedShape);
  }

  @Override
  protected void handleOverShapeDeselected(DrawShape lastOverShape) { 
    if (lastOverShape != selectedShape) { 
      resetDisplayMode(lastOverShape);
    }
  }

  @Override
  protected void handleOnIcon(DrawShape shape, int pos) {
    if (debug) {
      System.out.println("UMLPointTool.handleOnIcon() pos=" + pos);
    }

    if (shape instanceof BlockShape) { 
      BlockShape b = (BlockShape) shape; 
      if (b.isClosed()) { 
	b.open();
      } else {
	b.close();
      }
    }
  }

  protected void resetDisplayMode(DrawShape shape) {
    if (shape != null &&
	shape.getDisplayMode() != NORMAL) {
      shape.setDisplayMode(NORMAL);
      DrawShape c = shape.getParent();
      while (c != null && 
	     c instanceof Label) { 
	c = c.getParent();
      }
      if (c != null) { 
	//canvas.repaintShape(c, true);
      }
      canvas.repaintShape(shape, true);      
    }
  }

  protected void setDisplayMode(TextShape shape, int mode) {
    if (shape != null &&
	shape.getDisplayMode() != mode) {
      DrawShape c = shape.getParent();
      while (c != null && 
	     c instanceof Label) { 
	c = c.getParent();
      }
      if (c != null) { 
	Rectangle2D b1 = c.getBounds();
	Rectangle2D b2 = shape.getBounds();
	if (!b1.contains(b2)) { 
	  shape.setDisplayMode(mode); 
	  //canvas.repaintShape(c, true);
	  canvas.repaintShape(shape, true);
	}
      }
    }
  }

  @Override
  protected void handleDoubleClick(DrawShape shape) {
    if (debug) {
      System.out.println("UMLPointTool.handleDoubleClick()"); 
    }

    // delegate to tool handler 
    if (doubleClickHandler != null) { 
      doubleClickHandler.handleAction(doc, shape); 
    }
  }

  // for visual program 
  @Override
  protected void handleStatementPopup(DrawShape overShape) { 
    //System.out.println("UMLPointTool.handleStatementPopup() " + 
    //	       (overShape == null ? "null" : overShape.getClass().getName()));

    if (doc.getDocumentType() == Document.DocumentType.Visual_Class_Diagram) { 
      if (overShape instanceof TextShape) { 
	if (overShape != selectedShape) { 
	  setDisplayMode((TextShape) overShape, POPUP);
	}
      } else if (overShape instanceof BarShape) { 
	
      } else if (overShape instanceof StatementShape) {
	
      }
    }
  }

  public void setDoubleClickHandler(ToolHandler handler) { 
    doubleClickHandler = handler;
  }

  protected DrawApp main = null;

  protected HoverPopupManager hoverPopupManager = null;

  protected ToolHandler doubleClickHandler = null; 

  @Override
  protected void hidePopup(boolean hideThumbNail, boolean hideOverShapePopup) {
    if (debug) { 
      System.out.println("UMLPointTool.hidePopup() " + 
			 hideThumbNail + ":" + hideOverShapePopup);
    }

    super.hidePopup(hideThumbNail, hideOverShapePopup);
    if (hideOverShapePopup) { 
      hoverPopupManager.hidePopup();
    }
  }

  /**
   * Handle popup triggered by mouse hover over a shape 
   *
   * Return true if a popup is shown, false otherwise 
   */
  @Override
  protected boolean handleHoverPopup(DrawShape overShape, boolean altdown) {
    if (debug) { 
      System.out.println("UMLPointTool.handleHoverPopup() ");
    }

    Point p = mousePosition; 
    if (hoverPopupManager.insidePopup(p) ||
	hoverPopupManager.insideGapPopup(p)) { 
      return true;
    }

    if (overShape != null) { 
      hoverPopupManager.hideGapPopup();
      int ppos = hoverPopupManager.makeHoverPopup(overShape, pos, p, cur);

      if (ppos != -1) { 
	hoverPopupManager.showPopup();
	return true; 
      } else {
	if (hoverPopupManager.isInsidePopup(p)) { 
	  if (hoverPopupManager.isInsidePopup(p, 3) && 
	      hoverPopupManager.isPopupActive()) { 
	    hoverPopupManager.brightenPopup();
	  }
	  return true; 
	} 
      }
    } else { 
      if (hoverPopupManager.isInsidePopup(p)) { 
	if (hoverPopupManager.isInsidePopup(p, 3) && 
	    hoverPopupManager.isPopupActive()) { 
	  hoverPopupManager.brightenPopup();
	}
	return true; 
      } else { 
	hoverPopupManager.hidePopup();

	if (doc.isOutside(cur)) { 
	  hoverPopupManager.makeGapPopup(p);
	  hoverPopupManager.showGapPopup();
	}
	return false;
      }
    }
    return false;
  } 

  public void addRemovePolyShapeVertex(PolyShape polyshape, int position) { 
    if (polyshape != null) { 
      setSelectedShape(polyshape, position, false);
      handleAddRemoveVertex(polyshape);
    }
  }

  public void addRemoveCompartment(HorizontalCompartments hc, int position, boolean add) {
    if (debug) { 
      System.out.println("UMLPointTool.addRemoveCompartment() add=" + add);
    }
    if (hc != null) { 
      setSelectedShape((DrawShape) hc, position, false);
      pos1 = (add ? PointInHorizontalCompartment : PointOnHorizontalDivider);      
      handleAddRemoveCompartment(hc);	
    }
  }

  public void hideShowClassCompartments(ClassShape cshape, boolean show) { 
    if (cshape != null) { 
      if (show) {
	cshape.showAttributeOperationCompartments();
	layoutGraphEdgesAdjacentToCurrentNode();
      } else {
	cshape.updateModel();
	cshape.hideAttributeOperationCompartments();
	layoutGraphEdgesAdjacentToCurrentNode();
      }
      doc.shapeChanged(cshape);
      graph.layoutAdjacentEdges(cshape);
      view.refresh();
    }
  }


  //////////////////////////////////////////////////////////////////////////
  ///
  /// Handling popup for selected shape, trigger by right click or ctrl-click  
  ///
  //////////////////////////////////////////////////////////////////////////

  /**
   *  return context sensitive popup menu of the selected shape 
   *  trigger by right click or ctrl-click  
   */
  protected JPopupMenu getContextSensitivePopup() {
    if (moveShape != null) {
      ModelElement model = moveShape.getModel();
      if (model != null) {
	if (debug) {
	  System.out.println("getContextSensitivePopup(): model=" + model);
	}

	if (model instanceof UMLPackage) {

	} else if (model instanceof UMLClass) {
	  if (moveShape instanceof ClassShape) { 
	    return makeUMLClassPopup((ClassShape) moveShape, (UMLClass) model);
	  }
	} else if (model instanceof Enumeration) {
	  if (moveShape instanceof ClassShape) { 
	    return makeEnumPopup((ClassShape) moveShape, (Enumeration) model);
	  }
	} else if (model instanceof Note) {

	} else if (model instanceof UMLAssociation) {
	  UMLAssociation assoc = (UMLAssociation) model;
	  UMLAssociation.AssociationType atype = assoc.getType();
	  switch (atype) {
	  case Association:
	  case Aggregation:
	  case Composition:

	    break;
	  case Dependency:

	    break;
	  case UseCaseDependency:

	    break;

	  default:

	  }
	} else if (model instanceof UseCase) {

	} else if (model instanceof Actor) {

	} else if (model instanceof UMLSystemBoundary) {

	} else if (model instanceof UMLSystem) {

	} else if (model instanceof UMLObject) {

	} else if (model instanceof UMLMessage) {

	} else if (model instanceof State) {
	  State state = (State) model;
	  switch (state.getType()) {
	  case Simple_State:
	  case Hyper_State:
	  case Orthogonal_State:
	    if (moveShape instanceof StateShape) {
	      return makeUMLStatePopup((StateShape) moveShape, (State) model);
	    }
	    break;
	  default:
	  }
	} else if (model instanceof Transition) {

	} else if (model instanceof ActivityNode) {
	  ActivityNode anode = (ActivityNode) model;
	  switch (anode.getType()) {
	  case Computation:
	  case Object:
	  case DataStore:
	  case Accept_Event:
	  case Send_Event:

	    break;
	  case Decision:
	  default:

	  }
	} else if (model instanceof ActivityFlow) {

	} else if (model instanceof UMLInterface) {

	} else if (model instanceof LifeLine) {

	} else if (model instanceof ExecutionOccurance) {

	} else if (model instanceof HyperState) {

	} else if (model instanceof OrthogonalState) {

	} else if (model instanceof SubmachineState) {

	} else if (model instanceof xj.model.uml.Action) {

	} else if (model instanceof ActivityDiagram) {

	} else if (model instanceof ClassDiagram) {

	} else if (model instanceof SequenceDiagram) {

	} else if (model instanceof StateDiagram) {

	} else if (model instanceof UseCaseDiagram) {

	} else {

	}
      } else {
	// no model
	if (debug) {
	  System.out.println("getContextSensitivePopup(): model == null");
	}

      }
      if (moveShape instanceof Label) {
	if (debug) {
	  System.out.println("getContextSensitivePopup(): moveShape: Label");
	}

	GraphComponentShape owner = getGraphComponentOwner(label);
	if (owner instanceof GraphEdgeBase) {
	  GraphEdgeBase edge = (GraphEdgeBase) owner;

	  //System.out.println("getContextSensitivePopup() owner is edge " + edge);

	  model = edge.getModel();

	  //System.out.println("getContextSensitivePopup() owner model=" + model);
	    
	  if (model instanceof Transition) {
	    Label label = (Label) moveShape;
	    return makeTransitionLabelPopup(label, edge);
	  }
	}
      }

      if (moveShape instanceof GraphEdgeBase) {
	if (debug) {
	  System.out.println("getContextSensitivePopup(): moveShape: GraphEdgeBase");
	}

	GraphEdgeBase edge = (GraphEdgeBase) moveShape;
	return makeEdgePopup(edge);
      }

    }

    return null;
  }

  // action for popup menu, selected shape 
  class UMLClassPopupAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      //System.out.println("UMLClassPopupAction.actionPerformed()");
      Object source = e.getSource();
      if (source instanceof AbstractButton) {

	//System.out.println("UMLClassPopupAction.actionPerformed() @1");
	AbstractButton button = (AbstractButton) source;
	Container p = button.getParent();
	if (p instanceof JPopupMenu) { 
	  p.setVisible(false);
	} else if (hoverPopupManager != null) { 
	  hoverPopupManager.hidePopup(true);
	}

	ClassShape cls = shape;
	if (shape == null &&
	    moveShape instanceof ClassShape) {
	  cls = (ClassShape) moveShape;
	}
	String cmd = button.getActionCommand();
	hideShowClassCompartments(cls, "show".equals(cmd));
      }
    }
    ClassShape shape = null;
  }

  protected UMLClassPopupAction classAction = null;

  protected JPopupMenu classPopup1 = null;
  protected JPopupMenu classPopup2 = null;

  protected JPopupMenu makeUMLClassPopup(ClassShape shape, UMLClass model) {
    if (shape != null && model != null) {
      int n = shape.getNumberOfHorizontalCompartments();
      if (n == 1 && shape.isExpandable()) {
	if (classPopup1 == null) {
	  if (classAction == null) {
	    classAction = new UMLClassPopupAction();
	  }
	  classPopup1 = new JPopupMenu();
	  JMenuItem b1 = new JMenuItem(getResourceString("ShowAttr.text"));
	  b1.setFont(font);
	  b1.setActionCommand("show");
	  b1.addActionListener(classAction);
	  classPopup1.add(b1);
	}
	return classPopup1;
      } else if (n > 1 && shape.isCollapsable()) {
	if (classPopup2 == null) {
	  if (classAction == null) {
	    classAction = new UMLClassPopupAction();
	  }
	  classPopup2 = new JPopupMenu();
	  JMenuItem b1 = new JMenuItem(getResourceString("HideAttr.text"));
	  b1.setFont(font);
	  b1.setActionCommand("hide");
	  b1.addActionListener(classAction);
	  classPopup2.add(b1);
	}
	return classPopup2;
      }
    }
    return null;
  }

  protected JPopupMenu enumPopup1 = null;
  protected JPopupMenu enumPopup2 = null;

  protected JPopupMenu makeEnumPopup(ClassShape shape, Enumeration model) {
    if (shape != null && model != null) {
      int n = shape.getNumberOfHorizontalCompartments();
      if (n == 1) {
	if (enumPopup1 == null) {
	  if (classAction == null) {
	    classAction = new UMLClassPopupAction();
	  }
	  enumPopup1 = new JPopupMenu();
	  JMenuItem b1 = new JMenuItem(getResourceString("ShowEnumValues.text"));
	  b1.setFont(font);
	  b1.setActionCommand("show");
	  b1.addActionListener(classAction);
	  enumPopup1.add(b1);
	}
	return enumPopup1;
      } else {
	if (enumPopup2 == null) {
	  if (classAction == null) {
	    classAction = new UMLClassPopupAction();
	  }
	  enumPopup2 = new JPopupMenu();
	  JMenuItem b1 = new JMenuItem(getResourceString("HideEnumValues.text"));
	  b1.setFont(font);
	  b1.setActionCommand("hide");
	  b1.addActionListener(classAction);
	  enumPopup2.add(b1);
	}
	return enumPopup2;
      }
    }
    return null;
  }

  ///////////////

  protected UMLStatePopupAction stateAction = null;

  protected JPopupMenu statePopup = null;

  class UMLStatePopupAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      System.out.println("UMLPointToo.UMLStatePopupAction");

      Object source = e.getSource();
      if (source instanceof JMenuItem) {
	JMenuItem button = (JMenuItem) source;
	Container p = button.getParent();
	p.setVisible(false);

	String cmd = button.getActionCommand();
	if (("show".equals(cmd) || "hide".equals(cmd)) && 
	    moveShape instanceof StateShape) {
	  System.out.println("UMLPointToo.UMLStatePopupAction: StateShape " + cmd);
	  
	  StateShape ss = (StateShape) moveShape;
	  if ("show".equals(cmd)) {
	    ss.showActionCompartment();
	  } else if ("hide".equals(cmd)) {
	    ss.hideActionCompartment();
	  }
	} else if (moveShape instanceof OrthogonalStateShape) { 
	  System.out.println("UMLPointToo.UMLStatePopupAction: OrthogonalStateShape " + cmd + 
			     " pos2=" + pos2);

	  OrthogonalStateShape os = (OrthogonalStateShape) moveShape; 
	  if ("even".equals(cmd)) {
	    os.evenRegionHeight(); 
	  } else if ("add".equals(cmd)) {
	    os.addRegion(pos2);
	  } else if ("remove".equals(cmd)) {
	    os.removeHorizontalDivider(pos2);
	  } else if ("split".equals(cmd)) {
	    os.splitRegion(pos2);
	  } else if ("removeDiv".equals(cmd)) {
	    os.removeHorizontalDivider(pos2);
	  }
	}  

	layoutGraphEdgesAdjacentToCurrentNode();
	doc.shapeChanged(moveShape);
	refreshMoveShape();
      }
    }

    public void setPosition(int position, int pos1, int pos2) { 
      this.position = position;
      this.pos1 = pos1;
      this.pos2 = pos2;
    }

    int position; 
    int pos1, pos2; 
  }

  protected JMenuItem makeStateMenu(String key, String action) { 
    JMenuItem b1 = new JMenuItem(getResourceString(key));
    b1.setFont(font);
    b1.setActionCommand(action);
    b1.addActionListener(stateAction);
    return b1; 
  }

  protected JPopupMenu makeUMLStatePopup(StateShape shape, State model) {
    if (shape != null && model != null) {
      if (stateAction == null) {
	stateAction = new UMLStatePopupAction();
      }
      if (statePopup == null) {
	statePopup = new JPopupMenu();
      } else { 
	statePopup.removeAll();
      }

      if (!shape.isActionCompartmentShown()) {
	statePopup.add(makeStateMenu("ShowActions.text", "show"));
      } else {
	statePopup.add(makeStateMenu("HideActions.text", "hide"));
      }
      if (shape instanceof OrthogonalStateShape) { 
	statePopup.add(makeStateMenu("EvenCompartment.text", "even"));
	statePopup.add(makeStateMenu("AddCompartment.text", "add"));
	if (pos1 == PointOnHorizontalDivider) { 
	  statePopup.add(makeStateMenu("RemoveDivider.text", "removeDiv"));
	} else { 
	  statePopup.add(makeStateMenu("RemoveCompartment.text", "remove"));
	  statePopup.add(makeStateMenu("SplitCompartment.text", "split"));
	}
	stateAction.setPosition(pos, pos1, pos2); 
      }
      return statePopup;
    }
    return null;
  }

  ///////////// popup action on selected shape

  protected JPopupMenu edgePopup = null;
  protected JMenu      edgeShapeMenu = null;
  protected JMenuItem  edgeSwapMenu = null;
  protected JMenuItem  edgeFlipMenu = null;
  protected JMenuItem  edgeReverseMenu = null;
  protected JMenu      edgeAlignmentMenu = null;

  protected static Map<String, JMenuItem[]> edgeShapeActionsMap = 
    new HashMap<String, JMenuItem[]>();

  protected JPopupMenu makeEdgePopup(GraphEdgeBase edge) {
    if (edge != null) {
      if (edgePopup == null) {
	edgePopup = new JPopupMenu();
      } else { 
	edgePopup.removeAll();
      }

      if (edge instanceof GraphEdgeShape) { 
	JMenu menu = makeEdgeShapeMenu(edge);
	if (menu != null) { 	 
	  edgePopup.add(menu);
	}
      }

      /*
      ModelElement emodel = edge.getModel();
      if (emodel instanceof UMLAssociation) { //AssociationElement) { 
	if (edgeSwapMenu == null) { 
	  makeEdgeSwapMenu();
	}
	edgePopup.add(edgeSwapMenu);
      }
      */

      if (edge.isFlippable()) { 
	if (edgeFlipMenu == null) { 
	  makeEdgeFlipMenu();
	}
	edgePopup.add(edgeFlipMenu);
      }

      if (edgeReverseMenu == null) { 
	makeEdgeReverseMenu();
      }
      edgePopup.add(edgeReverseMenu);

      if (edgeAlignmentMenu == null) { 
	makeEdgeAlignmentMenu();
      }
      edgePopup.add(edgeAlignmentMenu);

      return edgePopup; 
    }
    return null;
  }

  class EdgeShapeAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      Object source = e.getSource();
      if (source instanceof JMenuItem) {
	JMenuItem button = (JMenuItem) source;
	if (moveShape instanceof GraphEdgeShape && 
	    doc instanceof GraphDocument) {
	  GraphEdgeShape edge = (GraphEdgeShape) moveShape;
	  GraphDocument graph = (GraphDocument) doc;
	  String command = button.getActionCommand();
	  //System.out.println("EdgeShapeAction.actionPerformed()  command=" + command);

	  DrawShapeFactory factory = DrawShapeFactory.getInstance();
	  DrawShape newShape = factory.makeShape(command);
	  if (newShape instanceof GraphEdgeBase) { 
	    ChangeEdgeShapeCommand cmd = new ChangeEdgeShapeCommand((GraphView) view, graph, edge, 
								    (GraphEdgeBase) newShape);
	    graph.executeCommand(cmd);

	    //Point2D p1 = edge.getEnd1();
	    //Point p = view.virtualToPhysical(p1);
	    //mouseLButtonPressed(view, p, 0);
	    //mouseLButtonReleased(view, p, 0);
	    //canvas.repaint();
	  }
	}
      }
    }
  }

  protected EdgeShapeAction edgeShapeAction = new EdgeShapeAction();

  protected JMenu makeEdgeShapeMenu(GraphEdgeBase edge) {
    if (edge != null) {
      if (edgeShapeMenu == null) {
	String text  = getResourceString("EdgeShape.text");
	edgeShapeMenu = new JMenu(text);
      } else { 
	edgeShapeMenu.removeAll();
      }

      int i;
      String edgeTempName = edge.getTemplateName(); 
      JMenuItem[] edgeShapeActions = edgeShapeActionsMap.get(edgeTempName); 
      if (edgeShapeActions == null) { 
	i = edgeTempName.indexOf('.');
	String templateName = edgeTempName.substring(0, i);
	Template template = main.getTemplate(templateName);
	String[] edgeToolNames = Template.getEdgeToolNames(edgeTempName);
	if (edgeToolNames != null) { 
	  edgeShapeActions = new JMenuItem[edgeToolNames.length];
	  for (i = 0; i < edgeToolNames.length; i++) { 
	    GraphEdgeTemplate et = template.getEdgeShapeTemplate(edgeToolNames[i]);
	    if (et != null) { 
	      String command = template.getName() + "." + et.name; 
	      String text = getResourceString(command + ".text");
	      //System.out.println("  command=" + command + "   text=" + text);	    
	      Icon icon = new ShapeIcon(et.edge, et.width, et.height);
	      DrawShapeFactory factory = DrawShapeFactory.getInstance();
	      if (et.edge != null &&
		  factory.getPrototype(command) == null) { 
		//System.out.println("UMLPointTool.makeEdgeShapeMenu(): add prototype command=" + command);

		et.edge.setTemplateName(command);
		factory.addPrototype(command, et.edge);
	      }

	      JMenuItem  button = new JMenuItem(text, icon);
	      button.addActionListener(edgeShapeAction);
	      button.setActionCommand(command);
	      edgeShapeActions[i] = button;
	    }
	  }
	  edgeShapeActionsMap.put(edgeTempName, edgeShapeActions); 
	}
      }
      if (edgeShapeActions != null && 
	  edgeShapeActions.length > 0) { 
	for (i = 0; i < edgeShapeActions.length; i++) {       
	  edgeShapeMenu.add(edgeShapeActions[i]);
	}
	return edgeShapeMenu;
      }
    }
    return null;
  }

  class SwapEdgeEndsAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (moveShape instanceof GraphEdgeBase && 
	  doc instanceof GraphDocument) {
	GraphEdgeBase edge = (GraphEdgeBase) moveShape;
	GraphDocument graph = (GraphDocument) doc;
	SwapEdgeEndsCommand cmd = new SwapEdgeEndsCommand((GraphView) view, graph, edge);
	graph.executeCommand(cmd);

	mouseLButtonPressed(view, mousePosition, 0);
	mouseLButtonReleased(view, mousePosition, 0);
	canvas.repaint();
      }
    }
  }

  protected SwapEdgeEndsAction edgeSwapAction = new SwapEdgeEndsAction();

  protected JMenuItem makeEdgeSwapMenu() {
    if (edgeSwapMenu == null) {
      String text  = getResourceString("EdgeSwap.text");
      edgeSwapMenu = new JMenuItem(text);
      edgeSwapMenu.addActionListener(edgeSwapAction);
    }
    return edgeSwapMenu;     
  }

  class ReverseEdgeDirectionAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (moveShape instanceof GraphEdgeBase && 
	  doc instanceof GraphDocument) {
	GraphEdgeBase edge = (GraphEdgeBase) moveShape;
	GraphDocument graph = (GraphDocument) doc;
	ReverseEdgeCommand cmd = new ReverseEdgeCommand((GraphView) view, graph, edge);
	graph.executeCommand(cmd);
      }
    }
  }

  protected ReverseEdgeDirectionAction edgeReverseAction = new ReverseEdgeDirectionAction();

  protected JMenuItem makeEdgeReverseMenu() { 
    if (edgeReverseMenu == null) {
      String text  = getResourceString("EdgeReverse.text");
      edgeReverseMenu = new JMenuItem(text);
      edgeReverseMenu.addActionListener(edgeReverseAction);
    }
    return edgeReverseMenu;
  }

  class FlipEdgeAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (moveShape instanceof GraphEdgeBase && 
	  doc instanceof GraphDocument) {
	GraphEdgeBase edge = (GraphEdgeBase) moveShape;
	GraphDocument graph = (GraphDocument) doc;
	FlipEdgeCommand cmd = new FlipEdgeCommand((GraphView) view, graph, edge);
	graph.executeCommand(cmd);
      }
    }
  }

  protected FlipEdgeAction edgeFlipAction = new FlipEdgeAction();

  protected JMenuItem makeEdgeFlipMenu() { 
    if (edgeFlipMenu == null) {
      String text  = getResourceString("EdgeFlip.text");
      edgeFlipMenu = new JMenuItem(text);
      edgeFlipMenu.addActionListener(edgeFlipAction);
    }
    return edgeFlipMenu;
  }

  class EdgeAlignmentAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      Object source = e.getSource();
      if (source instanceof JMenuItem) {
	JMenuItem button = (JMenuItem) source;
	Container p = button.getParent();
	p.setVisible(false);

	if (moveShape instanceof GraphEdgeBase && 
	    doc instanceof GraphDocument) {
	  GraphEdgeBase edge = (GraphEdgeBase) moveShape;
	  GraphDocument graph = (GraphDocument) doc;
	  String cmd = button.getActionCommand();
	  if ("Left".equals(cmd)) {
	    graph.alignEdge(edge, GraphDocument.Alignment.Left);
	  } else if ("Center".equals(cmd)) {
	    graph.alignEdge(edge, GraphDocument.Alignment.Center);
	  } else if ("Right".equals(cmd)) {
	    graph.alignEdge(edge, GraphDocument.Alignment.Right);
	  } else if ("Top".equals(cmd)) {
	    graph.alignEdge(edge, GraphDocument.Alignment.Top);
	  } else if ("Bottom".equals(cmd)) {
	    graph.alignEdge(edge, GraphDocument.Alignment.Bottom);
	  }
	  canvas.repaint();
	}
      }
    }
  }

  protected EdgeAlignmentAction edgeAlignmentAction = null;

  protected JMenu makeEdgeAlignmentMenu() { 
    if (edgeAlignmentMenu == null) {
      Icon leftIcon   = DrawApp.getIcon("jlfAlignLeftIcon");
      Icon rightIcon  = DrawApp.getIcon("jlfAlignRightIcon");
      Icon centerIcon = DrawApp.getIcon("jlfAlignCenterIcon");
      Icon topIcon    = DrawApp.getIcon("jlfAlignTopIcon");
      Icon bottomIcon = DrawApp.getIcon("jlfAlignBottomIcon");
      
      String alignText  = getResourceString("AlignEnds.text");
      String leftText   = getResourceString("Left.text");
      String centerText = getResourceString("Center.text");
      String rightText  = getResourceString("Right.text");
      String topText    = getResourceString("Top.text");
      String bottomText = getResourceString("Bottom.text");
      
      edgeAlignmentMenu = new JMenu(alignText);
      edgeAlignmentAction = new EdgeAlignmentAction();
      JMenuItem b = new JMenuItem();
      b.setIcon(centerIcon);
      b.setText(centerText);
      b.setActionCommand("Center");
      b.addActionListener(edgeAlignmentAction);
      edgeAlignmentMenu.add(b);
      b = new JMenuItem();
      b.setIcon(leftIcon);
      b.setText(leftText);
      b.setActionCommand("Left");
      b.addActionListener(edgeAlignmentAction);
      edgeAlignmentMenu.add(b);
      b = new JMenuItem();
      b.setIcon(rightIcon);
      b.setText(rightText);
      b.setActionCommand("Right");
      b.addActionListener(edgeAlignmentAction);
      edgeAlignmentMenu.add(b);
      b = new JMenuItem();
      b.setIcon(topIcon);
      b.setText(topText);
      b.setActionCommand("Top");
      b.addActionListener(edgeAlignmentAction);
      edgeAlignmentMenu.add(b);
      b = new JMenuItem();
      b.setIcon(bottomIcon);
      b.setText(bottomText);
      b.setActionCommand("Bottom");
      b.addActionListener(edgeAlignmentAction);
      edgeAlignmentMenu.add(b);
    }
    return edgeAlignmentMenu;
  }

  ////////////// Handling transition label popup

  class TransitionLabelAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      Object source = e.getSource();
      if (source instanceof JMenuItem) {
	JMenuItem button = (JMenuItem) source;
	Container p = button.getParent();
	p.setVisible(false);

	ModelListener listener = edge.getModelListener();
	if (listener instanceof UMLTransitionModelListener) {
	  UMLTransitionModelListener tl = (UMLTransitionModelListener) listener;
	  String cmd = button.getActionCommand();
	  if ("vsplit".equals(cmd)) {
	    tl.splitLabel(true);
	  } else if ("hsplit".equals(cmd)) {
	    tl.splitLabel(false);
	  } else if ("merge".equals(cmd)) {
	    tl.mergeLabel();
	  } else if ("hide-event".equals(cmd)) {
	    tl.hide(EVENT_MENU);
	  } else if ("hide-guard".equals(cmd)) {
	    tl.hide(GUARD_MENU);
	  } else if ("hide-action".equals(cmd)) {
	    tl.hide(ACTION_MENU);
	  } else if ("show-event".equals(cmd)) {
	    tl.show(EVENT_MENU);
	  } else if ("show-guard".equals(cmd)) {
	    tl.show(GUARD_MENU);
	  } else if ("show-action".equals(cmd)) {
	    tl.show(ACTION_MENU);
	  }
	  canvas.repaint();
	  // doc.shapeChanged(moveShape);
	  // doc.shapeChanged(edge);
	}
      }
    }

    GraphEdgeBase edge;

    Label label;
  }

  protected static final int EVENT_MENU = 0x1;

  protected static final int GUARD_MENU = 0x2;

  protected static final int ACTION_MENU = 0x4;

  protected JPopupMenu labelPopup1 = null;

  protected JPopupMenu labelPopup2 = null;

  // protected JPopupMenu[] hideLabelPopup = new JPopupMenu[8];
  // protected JPopupMenu[] showLabelPopup = new JPopupMenu[8];
  protected TransitionLabelAction labelAction = null;

  protected JPopupMenu makeTransitionLabelPopup(Label label, GraphEdgeBase edge) {
    JPopupMenu popup = null;
    if (label != null && edge != null) {
      if (labelAction == null) {
	labelAction = new TransitionLabelAction();
      }
      if (label instanceof TextShape) {
	if (labelPopup1 == null) {
	  labelPopup1 = new JPopupMenu();
	  JMenuItem b = new JMenuItem(getResourceString("SplitLabelV.text"));
	  b.setFont(font);
	  b.setActionCommand("vsplit");
	  b.addActionListener(labelAction);
	  labelPopup1.add(b);
	  b = new JMenuItem(getResourceString("SplitLabelH.text"));
	  b.setFont(font);
	  b.setActionCommand("hsplit");
	  b.addActionListener(labelAction);
	  labelPopup1.add(b);
	}
	popup = labelPopup1;
      } else if (label instanceof BoxLabel) {
	popup = getLabelMenu((BoxLabel) label);
      }
      labelAction.label = label;
      labelAction.edge = edge;
    }
    return popup;
  }

  protected JPopupMenu getLabelMenu(BoxLabel box) {
    if (box != null) {
      int hidemenu = 0;
      int showmenu = 0;
      if (box.getOrientation() == VERTICAL) {
	int n = box.getShapeCount();
	showmenu = 0x7;
	for (int i = 0; i < n; i++) {
	  DrawShape s = box.getShape(i);
	  if (s instanceof TextShape) {
	    TextShape t = (TextShape) s;
	    String text = t.getText();
	    String prompt = t.getPrompt();
	    if ("event".equals(prompt)) {
	      showmenu -= EVENT_MENU;
	      if (text == null || text.length() == 0) {
		hidemenu += EVENT_MENU;
	      }
	    } else if ("guard".equals(prompt)) {
	      showmenu -= GUARD_MENU;
	      if (text == null || text.length() == 0) {
		hidemenu += GUARD_MENU;
	      }
	    } else if ("action".equals(prompt)) {
	      showmenu -= ACTION_MENU;
	      if (text == null || text.length() == 0) {
		hidemenu += ACTION_MENU;
	      }
	    }
	  }
	}
      }
      return getLabelMenu(hidemenu, showmenu);
    }
    return null;
  }

  protected JPopupMenu getLabelMenu(int hidemenu, int showmenu) {
    JPopupMenu popup = null;
    if (hidemenu == 0 && showmenu == 0) {
      if (labelPopup2 == null) {
	labelPopup2 = new JPopupMenu();
	JMenuItem b = new JMenuItem(getResourceString("MergeLabel.text"));
	b.setFont(font);
	b.setActionCommand("merge");
	b.addActionListener(labelAction);
	labelPopup2.add(b);
      }
      popup = labelPopup2;
    } else {
      popup = new JPopupMenu();
      JMenuItem b = new JMenuItem(getResourceString("MergeLabel.text"));
      b.setFont(font);
      b.setActionCommand("merge");
      b.addActionListener(labelAction);
      popup.add(b);
      if ((hidemenu & EVENT_MENU) != 0) {
	b = new JMenuItem(getResourceString("HideEvent.text"));
	b.setFont(font);
	b.setActionCommand("hide-event");
	b.addActionListener(labelAction);
	popup.add(b);
      } else if ((showmenu & EVENT_MENU) != 0) {
	b = new JMenuItem(getResourceString("ShowEvent.text"));
	b.setFont(font);
	b.setActionCommand("show-event");
	b.addActionListener(labelAction);
	popup.add(b);
      }

      if ((hidemenu & GUARD_MENU) != 0) {
	b = new JMenuItem(getResourceString("HideGuard.text"));
	b.setFont(font);
	b.setActionCommand("hide-guard");
	b.addActionListener(labelAction);
	popup.add(b);
      } else if ((showmenu & GUARD_MENU) != 0) {
	b = new JMenuItem(getResourceString("ShowGuard.text"));
	b.setFont(font);
	b.setActionCommand("show-guard");
	b.addActionListener(labelAction);
	popup.add(b);
      }

      if ((hidemenu & ACTION_MENU) != 0) {
	b = new JMenuItem(getResourceString("HideAction.text"));
	b.setFont(font);
	b.setActionCommand("hide-action");
	b.addActionListener(labelAction);
	popup.add(b);
      } else if ((showmenu & ACTION_MENU) != 0) {
	b = new JMenuItem(getResourceString("ShowAction.text"));
	b.setFont(font);
	b.setActionCommand("show-action");
	b.addActionListener(labelAction);
	popup.add(b);
      }
    }
    return popup;
  }

  protected static GraphComponentShape getGraphComponentOwner(DrawShape s) {
    if (s != null) {
      DrawShape parent;
      do {
	parent = s.getParent();
	if (parent instanceof GraphComponentShape) {
	  GraphComponentShape cshape = (GraphComponentShape) parent;
	  if (cshape.getGraphComponent() != null) { 
	    return cshape; 
	  }
	}
	s = parent;
      } while (s != null);
    }
    return null;
  }

}

///////////////////// 
//UI customization 
///////////////////// 

class TextIcon implements Icon {

  protected String text; 

  protected Color color;

  protected Dimension dim;

  static public final Color defaultColor = Color.black;

  static public final Dimension defaultDim = new Dimension(12, 12);

  public TextIcon(String text) {
    this(text, null, null);
  }

  public TextIcon(String text, Color c) {
    this(text, c, null);
  }

  public TextIcon(String text, Color c, int width, int height) {
    this(text, c, new Dimension(width, height));
  }

  public TextIcon(String text, Color c, Dimension d) {
    this.text = text;
    this.color = c;
    this.dim = d;
    if (color == null) {
      color = defaultColor;
    }
    if (dim == null) {
      dim = defaultDim;
    }
  }

  public Color getColor() { 
    return color;
  }

  public void setColor(Color color) {
    this.color = color;
  }

  public void paintIcon(Component c, Graphics g, int x, int y) {
    Color oldColor = g.getColor();
    g.setColor(color);
    g.fillRect(x, y, 12, 12);
    //g.setColor(Color.white);
    g.setColor(Color.black);
    g.setFont(new Font("SansSerif", Font.BOLD, 12));
    g.drawString(text, x + 2, y + 10); 
    g.setColor(oldColor);
  }

  public int getIconWidth() {
    return dim.width;
  }

  public int getIconHeight() {
    return dim.height;
  }

}

